LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

UnityShader入门精要③(图形学数学基础)

2021/9/10

本文为学习UnityShader时所记录的笔记,供学习产出和日后复习使用。

学习资料为冯乐乐(问就是我女神)的《UnityShader入门精要》

这一张讲的是各类变换矩阵,以及如何将模型空间的物体一步步变换到屏幕空间实现光栅化

默认都有一定的线性代数基础,所以矩阵的乘法之类的基础知识直接略过


4.3 矢量

矢量本身不是很难的东西,这里直接以我的理解来记录矢量内容的重点。

矢量之间可以进行乘法,有点积(dot product)和叉积(cross product)两种种类。


矢量的点积(点乘)

在unityshader中,点积为dot(a,b)。

点积的结果为一个值。点积满足交换律。点积公式懂的都懂。

image-20210418114037370

重点是点积的几何意义(朋友面试被考到过被刷掉了hhh印象深刻):

求一个向量在另一个向量的投影

测量两个方向有多近

分解一个向量

确定两个向量是同向还是反向(方向相反点积小于0,方向垂直点积等于0,方向相同点积大于0)


矢量的叉积(叉乘)

叉积不满足交换律!叉积结果为垂直于原两个向量的所在平面的新向量。

image-20210418115836909

image-20210418120436013

其中有个公式比较特别,即axb的长度等于a和b的模的乘积乘以他们之间夹角的正弦值。

叉积的几何意义:

计算垂直于一个平面的矢量(法向量)。

用于定义左和右,定义里和外(abc三点连线取叉积,结果方向都相同则在三角形内)。


4.4 矩阵

逆矩阵转置矩阵的公式和性质记得好好复习。

正交矩阵的定义要好好理解。


4.5 各种变换

变换简述

缩放矩阵(scale)和旋转(rotation)矩阵皆为线性变换(linear transform),都能直接用3x3的矩阵来表示。

平移变换不为线性变换,无法使用3x3的矩阵来直接表示。

为了将旋转缩放和平移在同一个矩阵内表示,引入**齐次坐标(homogeneous)**的概念,将矩阵变为4x4。

合并了旋转和平移的变换称为仿射变换(affine transform)。

image-20210910095825353


齐次坐标

齐次坐标是将三维转换成四维的结果。

对于一个点来说,新增的w分量为1。

对于一个矢量来说,新增的w分量为0。

这样设置会导致使用4x4矩阵对一个点进行变换时,平移缩放旋转都会施加于该点。

而对一个向量进行变换时,平移的效果会被忽略(平移不会对方向矢量产生影响,即矢量没有位置属性)。


平移矩阵(translation)

image-20210910100556224

可以看出,平移矩阵并不是一个正交矩阵。

平移矩阵的逆矩阵是将三个平移系数加上负号(相当于是反向平移)。


缩放矩阵(scale)

image-20210910100831575

如果三个缩放系数k相等,则将这样的缩放称为统一缩放(uniform scale)。

统一缩放不会改变角度和比例信息(例如非统一缩放操作法线会得到错误结果)。

缩放帧的逆矩阵是使用原缩放系数的倒数进行缩放(相当于反向的缩放)。

缩放一般不是正交矩阵


旋转矩阵(rotation)

image-20210910101254512

image-20210910101315687

旋转矩阵的逆矩阵是旋转相反方向的矩阵。

旋转矩阵是正交矩阵,多个旋转矩阵的串联同样是正交矩阵。


复合矩阵:

可以将平移旋转缩放组合起来成为一个复杂的变换过程。

image-20210910101830740

我们使用的是列矩阵,阅读顺序是从右到左。

绝大多数情况下我们规定变换的顺序为先缩放,再旋转,最后平移。


4.6 坐标空间

坐标空间的变换

现如今有两个坐标空间,父坐标空间P和子坐标空间C。

将子坐标空间C下表示的点或矢量Ac转换到父坐标空间P下的表示为Ap。

将父坐标空间P下表示的点或矢量Bp转换到子坐标空间C下的表示为Bc。

image-20210910102454639

我们只需要知道新坐标空间的原点和坐标轴在原坐标空间中的表示就可以求出变换矩阵。

image-20210910103317871

因为矢量没有位置,所以不需要表示平移:

image-20210910103247635


模型空间(model space)

模型空间与某个模型(对象)有关,也称对象空间。

在模型空间经常使用前(forward)后(back)左(left)右(right)上(up)下(down)的概念。

模型空间为左手坐标系


世界空间(world space)

世界空间表示了我们所关心的最大的最外层次的空间,可以被用于表述绝对位置。

世界空间同样是左手坐标系

我们可以构造出模型变换(model transform)的矩阵,将顶点从模型空间变换到世界空间,这是顶点变换的第一步:

image-20210910111803948

即先缩放,后旋转,最后平移。


观察空间(view space)

观察空间可以被认为是特殊的模型空间,即摄像机对象的模型空间。

观察空间中使用的是右手坐标系,他的正前方指向-z轴的方向,用于表示深度(z分量越小深度越深)。

我们需要将摄像机看到的转换到屏幕上,也就是将观察空间转换到屏幕空间,中间的阶段被称为投影(projection)

我们可以想象相当于是将摄像机平移到原点,坐标轴与世界空间的坐标轴重合,得到观察变换的矩阵。

将顶点从世界空间变换到观察空间,这是顶点变换的第二步,被称为观察变换(view transform)

image-20210910112649588

但由于观察空间使用右手坐标系,因此我们还需要对z分量进行取反操作。

就好比这样:

image-20210910112821611

这样就是最终的观察变换矩阵了。


剪裁空间(clip space)

剪裁空间又称齐次剪裁空间,目标是对这块渲染图元进行剔除和剪裁(位于剪裁空间内的部分会被保留,位于空间外的部分会被剔除)

这块空间由视锥体决定,也就是unity的摄像机中前面的锥形范围。

视锥体有两种类型,涉及到两种投影类型,即透视投影(perspective projection)和正交投影(orthographic projection)

透视投影模拟了人眼看世界的方式,有近大远小的效果

正交投影则完全保留了物体的角度和距离。

image-20210910114703231

image-20210910114636278

其中视锥体剪裁平面的近剪裁平面(near clip plane)和远剪裁平面(far clip plane)决定了摄像机可以看到的深度范围。

这阶段的变换用于将观察空间的顶点转换到剪裁空间,这是顶点变换的第三步,被称为投影变换(projection transform)

投影变换并没有真正进行投影工作,而是在为投影做准备。经过投影变换的运算后,w分量将具有实际意义。

透视投影

unity中的fov(field of view)属性决定了视锥体竖直方向的张开角度。

而Clipping Plane中有Near和Far参数来让我们控制两个剪裁平面距离摄像机的远近。

因此两个剪裁平面的高度:

image-20210910115148320

image-20210910115132901

我们还需要横纵比信息,我们假设当前横纵比为Aspect。

image-20210910115249403

根据已知的以上参数,我们可以推导出透视投影的投影矩阵:

image-20210910115337976

我们的投影矩阵是建立在unity上的,因此此时针对的观察空间是右手系的,使用列矩阵在矩阵右侧进行相乘。

变换后的z分量范围在[-w,w]之间。而在DirectX中变换后的z分量范围在[0,w]之间。

将一个顶点和透视投影的矩阵相乘的结果为:

image-20210910115614888

我们相当于是对原先的xyz分量进行了不同程度的缩放,而原先的w分量不再是1,而是原先z分量的取反结果。

若一个顶点在视锥体之内,变换后的坐标必须满足:

image-20210910115758919

不满足以上不等式的图元就会被剔除或剪裁。


正交投影

正交投影的视锥体为长方形,计算更为简单。

Camera组件的size为视锥体竖直方向上的一半。

而Clipping Plane中有Near和Far参数来让我们控制两个剪裁平面距离摄像机的远近。

因此两个剪裁平面的高度:

image-20210910120159041

image-20210910120225236

横纵比信息:

image-20210910120318327

因此正交投影矩阵如下:

image-20210910120347588

一个顶点与正交投影矩阵相乘的结果为:

image-20210910120418717

与透视投影不同,使用正交投影进行变换后,w分量依然为1


屏幕空间(screen space)

这一步所进行的是真正的投影,我们需要将视锥体投影到屏幕空间。

首先我们进行标准齐次除法(homogeneous division),也称透视除法

即用齐次坐标系的w分量去除以xyz分量,在OpenGL中这一步会得到归一化的设备坐标(normalized device coordinate,NDC)

经过透视投影变换后的剪裁空间会变换到一个立方体内,按照OpenGL的标准此时xyz分量范围为[-1,1],而DirectX中z分量范围是[0,1]

而在正交投影变换后的剪裁空间本身就是一个立方体了,因此齐次除法过后顶点的w分量为1,xyz不产生影响。

unity采用的是OpenGL这样的齐次剪裁空间,在左下角像素坐标为(0,0),右上角为(pixelWidth,pixelHeight)

齐次除法和屏幕映射的过程使用以下公式来总结:

image-20210910121417036

此时得到的就是最终一个顶点在屏幕空间的位置(二维)

上面的式子对xy分量做了处理,而z分量则会被用于深度缓冲。

在unity中,裁剪空间到屏幕空间的转换由底层为我们实现,我们的顶点着色器只需要完成将顶点坐标转换到剪裁空间。


总结

以上过程描述了一个顶点如何从模型空间变换到屏幕空间中的二维坐标

总结一下就是这样:

image-20210910123431856

顶点着色器的基本任务是把顶点坐标从模型空间转换到剪裁空间,对应了前三个过程(MVP变换

片元着色器则能得到该片元在屏幕空间的像素位置。

变换过程中(unity),只有观察空间使用了右手坐标系


4.7 法线变换

顶点一般会携带额外的信息,其中就包括顶点法线,当我们变换模型时它的法线也得进行变换。

我们上文说过,如果使用变换矩阵直接变换法线,可能会引起错误的结果

进行一些巴拉巴拉的推导后,我们发现,使用原变换矩阵的逆转置矩阵来变换法线可以得到正确结果

如果变换矩阵为正交矩阵,即只包含旋转变换,也可以直接用变换矩阵来变换法线。

因为正交矩阵的逆矩阵和转置矩阵相同,逆转置矩阵则是原矩阵。

若变换矩阵只包含旋转和统一缩放,而不包含非统一缩放,则我们可以利用同一缩放系数k来得到变换矩阵

即:

image-20210910124627119


2021.9.10 12:46完成,写了大概三小时。

做完知识的产出之后感觉整个体系都清晰起来了,以后要坚持写。